import { ApiCall, BaseConnection, PrefixLogger } from "tsrpc";
import { MsgUpdateSubWorldState } from "../../shared/protocols/worldServer/admin/MsgUpdateSubWorldState";
import { ServiceType } from "../../shared/protocols/serviceProto_worldServer";
import { IUserData, SubWorldData } from "../../shared/types/SubWorldData";
import { worldServer } from "../../WorldServerMain";
import { WorldServerConn } from "../WorldServer";
import { SubWorldConfigItem } from "../../shared/SubWorldConfig";
import { ReqReady, ResReady } from "../../shared/protocols/worldServer/PtlReady";
import { ReqJoinGame, ResJoinGame } from "../../shared/protocols/worldServer/PtlJoinGame";

/**
 * @en each subworld stands for a battle level/table or a game room
 * @zh 每一个 SubWorld 表示一个战斗关卡，一张球桌或者一个游戏房间
 */
export class SubWorld {
    /**
     * @en for console debugging
     * @zh 用于控制台输出
    */
    logger: PrefixLogger;

    /***
     * @en common properties for all subworld.
     * note, these properties are often used to sync to clients.
     * the specific subworld may add new properties on needs.
     * @zh 所有子世界通用的属性，注意，这些属性一般会用于同步给客户端
     * 具体的子世界有可能会根据需求新增。
     */
    protected _data: SubWorldData = {
        id: '',
        displayId: '',
        name: '',
        maxUser: 0,
        messages: [],
        userList: [],
        maxPlayerNum: 0,
        playerNum: 0,
        isPlaying: false,
    };

    private _password: string | undefined;

    /**
     * @en key:uid, value:userdata
     * @zh 方便使用 uid 查询用户数据
     **/
    protected _userMap: Map<string, IUserData> = new Map<string, IUserData>();

    /**
     * @en configuration of subworld
     * @zh 子世界的基本配置
    */
    config: SubWorldConfigItem;

    /**
     * @en user connections in this subworld
     * @zh 这个子世界的用户链接
     */
    conns: WorldServerConn[] = [];

    /**
     * @en undefined means this subworld is not empty. use for closing empty subworlds.
     * @zh 上一次空房的时间（undefined 代表房内有人）,用于定时解散无人的子世界
     */
    lastEmptyTime?: number;

    /**
     * @en undefined means not in match queue.
     * @zh 开始匹配的时间，`undefined` 代表不在匹配中
     */
    startMatchTime?: number;

    /** 
     * @en the last update time of this subworld's data.
     * @zh 这个子世界的信息的最后更新时间 
     * */
    lastUpdateTime: number;

    constructor(id: string, displayId: string, name: string, config: SubWorldConfigItem, password?: string | undefined) {
        this._data.id = id;
        this._data.displayId = displayId;
        this._data.name = name;
        this.config = config;
        this._password = password;
        this.lastEmptyTime = undefined;
        this.startMatchTime = undefined;
        this.lastUpdateTime = Date.now();
        this.logger = new PrefixLogger({
            logger: worldServer.logger,
            prefixs: [`[SubWorld ${id}]`],
        });
    }

    protected get data() {
        return this._data;
    }

    get id(): string {
        return this._data.id;
    }

    get maxUser(): number {
        return this._data.maxUser;
    }

    get messages() {
        return this._data.messages;
    }

    /**
     * @en current state of this subworld，sync to master server
     * @zh 子世界的当前状态，用于同步给 master 服务器
    */
    get state(): MsgUpdateSubWorldState['subWorlds'][number] {
        return {
            id: this._data.id,
            displayId: this._data.displayId,
            configId: this.config.id,
            name: this._data.name,
            userNum: this.conns.length,
            maxUserNum: this._data.maxUser,
            playerNum: this._data.playerNum,
            maxPlayerNum: this._data.maxPlayerNum,
            startMatchTime: this.startMatchTime,
            updateTime: this.lastUpdateTime,
            password: this._password
        }
    }

    /**
     * @en current user num
     * @zh 当前用户数量
    */
    get userNum(): number {
        return this._data.userList.length;
    }

    /**
     * @en broadcast message to all connections of this subworld
     * @zh 在子世界内广播 
     */
    broadcastMsg<T extends keyof ServiceType['msg']>(msgName: T, msg: ServiceType['msg'][T]) {
        return worldServer.server.broadcastMsg(msgName, msg, this.conns);
    }

    /**
     * @en listen messages from clients,should be overrded by sub class.
     * @zh 监听来自客户端的消息，子类会重写
     */
    listenMsgs(conn: WorldServerConn) {
    }

    /**
 * @en unlisten message handlers,should be overrded by sub class.
 * @zh 取消监听，子类会重写
 */
    unlistenMsgs(conn: WorldServerConn) {
    }

    /**
     * @en return whether the given uid refer to a player
     * @zh 返回 uid 对应的是否为玩家 
     */
    isPlayer(uid: string) {
        let user = this._userMap.get(uid);
        if (!user) {
            return false;
        }

        return user.isPlayer;
    }

    /**
     * @en return whether the given uid refer to a watcher
     * @zh 返回 uid 对应的是否为观众
     */
    isWatcher(uid: string) {
        let user = this._userMap.get(uid);
        if (!user) {
            return false;
        }

        return !user.isPlayer;
    }

    setReady(user: IUserData, value: boolean) {
        if (user.ready == true) {
            return;
        }
        user.ready = value;
        this.broadcastMsg("UserDataChangedPush", { uid: user.uid, ready: value });
        this.onCheckGameBegin();
    }

    onUserEnter(conn: WorldServerConn) { }

    onPlayerLeave(conn: WorldServerConn) { }

    onJoinGame(newPlayer: IUserData) { return false; }

    onCheckGameBegin() { }

    onResetGameData() { }

    onDisconnected(conn: WorldServerConn) {
        const currentUser = conn.currentUser;
        if (!currentUser) {
            return;
        }
        let user = this._userMap.get(currentUser.uid);
        if (user) {
            user.isOnline = false;
            this.broadcastMsg("UserDataChangedPush", { uid: currentUser.uid, isOnline: user.isOnline });
        }
    }

    update() {
        if (!this.config.isPublic && this.lastEmptyTime && (Date.now() - this.lastEmptyTime >= 10000)) {
            this.destroy();
        }
    }

    destroy() {
        this.logger.log('[Destroy]');

        worldServer.subWorlds.removeOne(v => v.id === this.id);
        worldServer.id2SubWorld.delete(this.id);
    }

    // ================= called by network ================
    /***
     * @en called by rpc when a user wants to take a seat and be a player.
     * @zh 当用户想要占一个位置，成为玩家时，会被 rpc 调用
     */
    onRPC_JoinGame(call: ApiCall<ReqJoinGame, ResJoinGame>) {
        const conn = call.conn as WorldServerConn;

        let currentUser = conn.currentUser;

        if (!currentUser) {
            return call.error('INVALID_USER');
        }
        if (this._data.playerNum == this._data.maxPlayerNum) {
            return call.error('NO_EMPTY_SEAT');
        }
        let user = this._userMap.get(currentUser.uid);
        if (!user) {
            return call.error('INVALID_USER_DATA');
        }

        if (this.isPlayer(currentUser.uid)) {
            return call.error('HAS_SIT_DOWN');
        }

        let isPlayer = this.onJoinGame(user);
        if (isPlayer) {
            user.isPlayer = isPlayer;
            this._data.playerNum++;
            this.broadcastMsg("SubWorldDataChangedPush", { numPlayer: this._data.playerNum });
            this.broadcastMsg("UserDataChangedPush", { uid: currentUser.uid, isPlayer: isPlayer });
        }
        call.succ({});
    }

    onRPC_UserReady(call: ApiCall<ReqReady, ResReady>) {
        const conn = call.conn as WorldServerConn;

        let currentUser = conn.currentUser;

        if (!currentUser) {
            return call.error('INVALID_USER');
        }

        let user = this._userMap.get(currentUser.uid);
        if (!user) {
            return call.error('INVALID_USER_DATA');
        }

        let value = true;
        this.setReady(user, value);
        call.succ({});
    }

    onRPC_Enter(conn: WorldServerConn) {
        this.lastEmptyTime = 0;

        const currentUser = conn.currentUser;
        if (!currentUser) {
            return;
        }
        this.logger.log('[UserEnter]', currentUser?.uid);

        let user = this._userMap.get(currentUser.uid);
        if (!user) {
            user = {
                uid: currentUser.uid,
                isOnline: true,
            }
            this._data.userList.push(user);
            this._userMap.set(currentUser.uid, user);
        }
        else {
            user.isOnline = true;
        }

        this.broadcastMsg("UserComesToTablePush", user);
        conn.sendMsg("SubWorldDataSyncPush", { data: this._data });
        this.onUserEnter(conn);
    }

    onRPC_Leave(conn: WorldServerConn) {
        const currentUser = conn.currentUser;
        if (!currentUser) {
            return;
        }
        this.logger.log('[UserLeave]', currentUser?.uid);

        /**
        * @en if game is playing, game over. otherwise, clean seat.
        * @zh 如果游戏正在进行中，则游戏结束。如果没有开始，则清理坐位信息。 
        **/
        for (let i = 0; i < this._data.userList.length; ++i) {
            let u = this._data.userList[i];
            if (u.uid == currentUser.uid) {
                if (u.isPlayer) {
                    this.onPlayerLeave(conn);
                    this._data.playerNum--;
                    this.broadcastMsg("SubWorldDataChangedPush", { numPlayer: this._data.playerNum });
                }
                this._data.userList.splice(i, 1);
                this._userMap.delete(u.uid);
                break;
            }
        }

        this.broadcastMsg("UserLeavesFromTablePush", { uid: currentUser.uid });

        //remove the link.

        this.conns.removeOne(v => v === conn);
        //this._data.users.removeOne(v => v.uid === currentUser.uid);
        //delete this.players[currentUser.uid]
        this.lastUpdateTime = Date.now();

        if (conn) {
            this.unlistenMsgs(conn);
        }

        if (this.conns.length === 0) {
            this.lastEmptyTime = Date.now();
        }
    }
}